//Generator 是实现状态机的最佳结构。比如，下面的clock函数就是一个状态机。

var ticking = true;
var clock = function() {
	if (ticking)
		console.log('Tick!');
	else
		console.log('Tock!');
	ticking = !ticking;
}


/*
上面代码的clock函数一共有两种状态（Tick和Tock），每运行一次，就改变一次状态。
这个函数如果用 Generator 实现，就是下面这样。

Generator 实现与 ES5 实现对比，可以看到少了用来保存状态的外部变量ticking，这样就更简洁，
更安全（状态不会被非法篡改）、更符合函数式编程的思想，在写法上也更优雅。
Generator 之所以可以不用外部变量保存状态，是因为它本身就包含了一个状态信息，即目前是否处于暂停态。
*/
var clock = function*() { //666  不过无数个状态真的好吗
	while (true) {
		console.log('Tick!');
		yield;
		console.log('Tock!');
		yield;
	}
};



/*
协程（coroutine）是一种程序运行的方式，可以理解成“协作的线程”或“协作的函数”。
协程既可以用单线程实现，也可以用多线程实现。前者是一种特殊的子例程，后者是一种特殊的线程。

（1）协程与子例程的差异

传统的“子例程”（subroutine）采用堆栈式“后进先出”的执行方式，只有当调用的子函数完全执行完毕，才会结束执行父函数。
协程与其不同，多个线程（单线程情况下，即多个函数）可以并行执行，但是只有一个线程（或函数）处于正在运行的状态，
其他线程（或函数）都处于暂停态（suspended），线程（或函数）之间可以交换执行权。
也就是说，一个线程（或函数）执行到一半，可以暂停执行，将执行权交给另一个线程（或函数），
等到稍后收回执行权的时候，再恢复执行。这种可以并行执行、交换执行权的线程（或函数），就称为协程。

从实现上看，在内存中，子例程只使用一个栈（stack），而协程是同时存在多个栈，但只有一个栈是在运行状态，
也就是说，协程是以多占用内存为代价，实现多任务的并行。


（2）协程与普通线程的差异

不难看出，协程适合用于多任务运行的环境。在这个意义上，它与普通的线程很相似，都有自己的执行上下文、可以分享全局变量。
它们的不同之处在于，同一时间可以有多个线程处于运行状态，但是运行的协程只能有一个，其他协程都处于暂停状态。
此外，普通的线程是抢先式的，到底哪个线程优先得到资源，必须由运行环境决定，但是协程是合作式的，执行权由协程自己分配。

由于 JavaScript 是单线程语言，只能保持一个调用栈。引入协程以后，每个任务可以保持自己的调用栈。
这样做的最大好处，就是抛出错误的时候，可以找到原始的调用栈。不至于像异步操作的回调函数那样，一旦出错，原始的调用栈早就结束。

Generator 函数是 ES6 对协程的实现，但属于不完全实现。Generator 函数被称为“半协程”（semi-coroutine），
意思是只有 Generator 函数的调用者，才能将程序的执行权还给 Generator 函数。如果是完全执行的协程，
任何函数都可以让暂停的协程继续执行。

如果将 Generator 函数当作协程，完全可以将多个需要互相协作的任务写成 Generator 函数，它们之间使用yield表达式交换控制权。
*/









/*
Generator 与上下文

JavaScript 代码运行时，会产生一个全局的上下文环境（context，又称运行环境），包含了当前所有的变量和对象。
然后，执行函数（或块级代码）的时候，又会在当前上下文环境的上层，产生一个函数运行的上下文，变成当前（active）的上下文
，由此形成一个上下文环境的堆栈（context stack）。
这个堆栈是“后进先出”的数据结构，最后产生的上下文环境首先执行完成，退出堆栈，然后再执行完成它下层的上下文，直至所有代码执行完成，堆栈清空。

Generator 函数不是这样，它执行产生的上下文环境，一旦遇到yield命令，就会暂时退出堆栈，但是并不消失，
里面的所有变量和对象会冻结在当前状态。等到对它执行next命令时，这个上下文环境又会重新加入调用栈，冻结的变量和对象恢复执行。
*/

function* gen() {
	yield 1;
	return 2;
}

let g = gen();

console.log(
	g.next().value,
	g.next().value,
);

/*
next()为启动作用函数的作用  yield为冻结   return 和 yield yield* 的区别要明朗 

上面代码中， 第一次执行g.next() 时， Generator 函数gen的上下文会加入堆栈， 即开始运行gen内部的代码。
 等遇到yield 1 时， gen上下文退出堆栈， 内部状态冻结。 第二次执行g.next() 时， gen上下文重新加入堆栈， 
 变成当前的上下文， 重新恢复执行。
 */
